package com.szjz.elasticsearch.eshelper.base.wrapper;

import com.szjz.elasticsearch.eshelper.base.BaseESEntity;
import com.szjz.elasticsearch.eshelper.base.wrapper.service.WrapperService;
import com.szjz.elasticsearch.eshelper.page.Page;
import com.szjz.elasticsearch.eshelper.wrapper.ESQueryWrapper;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.core.CountRequest;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.reindex.BulkByScrollResponse;
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.Date;
import java.util.List;

/**
 * @author 史博
 * @desc 包装服务实现类
 * @date 2021/1/19 下午4:55 周二
 */

@Slf4j
@Service
public class WrapperServiceImpl<T extends BaseESEntity> extends MapWrapperServiceImpl<T> implements WrapperService<T> {

	/**
	 * 查询所有
	 * 当数据量大的时候不建议使用该方法
	 *
	 * @return 数据集合
	 */
	@Override
	public List<T> selectAll() {
		SearchRequest searchRequest = new SearchRequest(getIndexName());
		return getSelectTs(null, searchRequest, null);
	}

	/**
	 * 通过包装类查询集合
	 *
	 * @param wrapper 将要查询的条件封装到wrapper中
	 * @return 数据集合
	 */
	@Override
	public List<T> select(ESQueryWrapper<T> wrapper) {
		return select(wrapper, SIZE);
	}

	/**
	 * 通过包装类查询按天分片的索引库集合
	 *
	 * @param wrapper 将要查询的条件封装到wrapper中
	 * @param date    索引库对应的分片时间
	 * @return 某一天的数据集合
	 */
	@Override
	public List<T> select(ESQueryWrapper<T> wrapper, Date date) {
		return select(wrapper, date, SIZE);
	}


	/**
	 * 通过包装类查询按天分片的索引库集合
	 *
	 * @param wrapper   将要查询的条件封装到wrapper中
	 * @param startDate 开始时间
	 * @param endDate   结束时间
	 * @return 某一时间段的数据集合
	 */
	@Override
	public List<T> select(ESQueryWrapper<T> wrapper, Date startDate, Date endDate) {
		return select(wrapper, startDate, endDate, SIZE);
	}

	/**
	 * 通过包装类查询集合
	 *
	 * @param wrapper 将要查询的条件封装到wrapper中
	 * @param size    返回结果集的最大数量
	 * @return 数据集合
	 */
	@Override
	public List<T> select(ESQueryWrapper<T> wrapper, Integer size) {
		SearchRequest searchRequest = new SearchRequest(getIndexName());
		return getSelectTs(wrapper, searchRequest, size);
	}

	/**
	 * 通过包装类查询按天分片的索引库集合
	 *
	 * @param wrapper 将要查询的条件封装到wrapper中
	 * @param date    索引库对应的分片时间
	 * @param size    返回结果集的最大数量
	 * @return 某一天的数据集合
	 */
	@Override
	public List<T> select(ESQueryWrapper<T> wrapper, Date date, Integer size) {
		SearchRequest searchRequest = new SearchRequest(getIndexName(date));
		return getSelectTs(wrapper, searchRequest, size);
	}

	/**
	 * 通过包装类查询按天分片的索引库集合
	 *
	 * @param wrapper   将要查询的条件封装到wrapper中
	 * @param startDate 开始时间
	 * @param endDate   结束时间
	 * @param size      返回结果集的最大数量
	 * @return 某一时间段的数据集合
	 */
	@Override
	public List<T> select(ESQueryWrapper<T> wrapper, Date startDate, Date endDate, Integer size) {
		SearchRequest searchRequest = getSearchRequestAcDay(startDate, endDate);
		return getSelectTs(wrapper, searchRequest, size);
	}

	/**
	 * 分页查询-通过包装类查询集合
	 *
	 * @param wrapper  将要查询的条件封装到wrapper中
	 * @param pageNum  当前页
	 * @param pageSize 每页容量
	 * @return
	 */
	@Override
	public Page<T> page(ESQueryWrapper<T> wrapper, Integer pageNum, Integer pageSize) {
		SearchRequest searchRequest = new SearchRequest(getIndexName());
		return getPage(wrapper, pageNum, pageSize, searchRequest);
	}

	/**
	 * 分页查询-通过包装类查询集合
	 * 适用于按天分片的索引库
	 *
	 * @param wrapper  将要查询的条件封装到wrapper中
	 * @param date     时间
	 * @param pageNum  当前页
	 * @param pageSize 每页容量大小
	 * @return
	 */
	@Override
	public Page<T> page(ESQueryWrapper<T> wrapper, Date date, Integer pageNum, Integer pageSize) {
		SearchRequest searchRequest = new SearchRequest(getIndexName(date));
		return getPage(wrapper, pageNum, pageSize, searchRequest);
	}


	/**
	 * 分页查询-通过包装类查询集合
	 * 适用于按天分片的索引库
	 *
	 * @param wrapper   将要查询的条件封装到wrapper中
	 * @param startDate 开始时间
	 * @param endDate   结束时间
	 * @param pageNum   当前页
	 * @param pageSize  每页容量大小
	 * @return
	 */
	@Override
	public Page<T> page(ESQueryWrapper<T> wrapper, Date startDate, Date endDate, Integer pageNum, Integer pageSize) {
		SearchRequest searchRequest = getSearchRequestAcDay(startDate, endDate);
		return getPage(wrapper, pageNum, pageSize, searchRequest);
	}

	/**
	 * 通过包装类删除数据-单库
	 *
	 * @param wrapper 将要查询的条件封装到wrapper中
	 * @return
	 */
	@Override
	@SneakyThrows
	public Long delete(ESQueryWrapper<T> wrapper) {
		DeleteByQueryRequest request = new DeleteByQueryRequest(getIndexName());
		return deleteBuilder(wrapper, request);
	}

	/**
	 * 通过包装类删除数据-按天分库
	 *
	 * @param wrapper 将要查询的条件封装到wrapper中
	 * @param startDate 开始时间
	 * @param endDate   结束时间
	 * @return
	 */
	@Override
	@SneakyThrows
	public Long deleteAcDay(ESQueryWrapper<T> wrapper, Date startDate, Date endDate) {
		String[] indexNamesAcDay = getIndexNamesAcDay(startDate, endDate);
		// 支持同时操作多个索引
		DeleteByQueryRequest request = new DeleteByQueryRequest(indexNamesAcDay);
		return deleteBuilder(wrapper, request);
	}

	/**
	 * 通过包装类删除集合-按月分库
	 *
	 * @param wrapper   将要查询的条件封装到wrapper中
	 * @param startDate 开始时间
	 * @param endDate   结束时间
	 * @return
	 */
	@Override
	public Long deleteAcMouth(ESQueryWrapper<T> wrapper, Date startDate, Date endDate) throws IOException {
		String[] indexNamesAcMonth = getIndexNamesAcMonth(startDate, endDate);
		// 支持同时操作多个索引
		DeleteByQueryRequest request = new DeleteByQueryRequest(indexNamesAcMonth);
		return deleteBuilder(wrapper, request);
	}

	private long deleteBuilder(ESQueryWrapper<T> wrapper, DeleteByQueryRequest request) throws IOException {
		// 根据多个条件 生成 boolQueryBuilder
		BoolQueryBuilder queryBuilder = getBaseBoolQueryBuilder(wrapper.getConditions());
		request.setQuery(queryBuilder);
		// proceed - 忽略版本冲突，继续执行
		request.setConflicts("proceed");
		// 可以限制批量删除文档的数量
		// request.setMaxDocs(size);
		BulkByScrollResponse bulkResponse = restHighLevelClient.deleteByQuery(request, RequestOptions.DEFAULT);
		log.info("【批量删除】成功删除文档数量 : {}", bulkResponse.getDeleted());
		log.info("【批量删除】耗时 : {}", bulkResponse.getTook());
		log.info("【批量删除】{}", bulkResponse.isTimedOut() ? "已超时" : "未超时");
		log.debug("【批量删除】设计文档总数 {}", bulkResponse.getTotal());
		log.debug("【批量删除】版本冲突次数 {}", bulkResponse.getVersionConflicts());
		return bulkResponse.getDeleted();
	}

	/**
	 * count 通过包装类查询数量
	 *
	 * @param wrapper 将要查询的条件封装到ESQueryWrapper中
	 * @return
	 */
	@Override
	public Long count(ESQueryWrapper<T> wrapper) {
		CountRequest countRequest = new CountRequest(getIndexName());
		return getCount(countRequest,wrapper);
	}

}
